#include "SDL/SDL.h"
#include "SDL/SDL_opengl.h"
#include <iostream>
#include <stdlib.h>
#include "../include/interface.h"
#include "../include/main.h"
#include "../include/Global.h"



InputType TurretIsPlaced;  //Will save what kind of turret was built




void ProcessObjects(GLuint *selectBuff, GLint  hits)
{
    int selectBuffPt = 0;
    int namesOnStack;
    bool noMin = false;
    float minZ = 0;
    int name;
    int index;
    int tempHits = hits;

    //loop while you have hits
    //Select buffer has these values
    //selectBuff[N0] = number of names on the stack
    //selectBuff[N0 + 1] = minimum Z value
    //selectBuff[N0 + 2] = maximum Z value
    //selectBuff[N0 + 3~number of names on stack] = the names on the stack
    ///
    while(tempHits > 0)
    {
        namesOnStack = selectBuff[selectBuffPt];

        if(noMin == false && namesOnStack > 0)
        {
            minZ = selectBuff[selectBuffPt + 1];
            noMin = true;
        }
        if(selectBuff[selectBuffPt + 1] < minZ && namesOnStack > 0)
        {
            minZ = selectBuff[selectBuffPt + 1];
            name = selectBuff[selectBuffPt + 3];
            if(namesOnStack > 1)
                index = selectBuff[selectBuffPt + 4];
        }
        selectBuffPt += namesOnStack + 3;
        tempHits--;
    }
////////////////Debugging code/////////////////////
    std::cout << "Hits: " << hits << std::endl;//

    for(int i = 0; i < 64; i++)
    {
        std::cout << selectBuff[i] << " ";
    }
    std::cout << std::endl;

    std::cout << "name " << name << std::endl; //
///////////////////////////////////////////////////

    switch (name)
    {
    case nGROUND:
        std::cout << "Ground" << std::endl;
        break;

    case nHERO:

        break;

    case nMINION:
        std::cout << "Minion #" << index << std::endl;
        break;

    case nTURRET:
        std::cout << "Turret #" << index;
        if(turret[index].GetType() == Standard)
            std::cout << " Type: Standard" << endl;
        if(turret[index].GetType() == Slow)
            std::cout << " Type: Slow" << endl;
        if(turret[index].GetType() == AoE)
            std::cout << " Type: AoE" << endl;
        if(turret[index].GetType() == Wall)
            std::cout << " Type: Wall" << endl;
        break;

    case nBULLET:
        std::cout << "Bullet" << std::endl;
        break;
    case nMAP:
        std::cout << "MAP!!" << std::endl;
        break;
    }
}

// Process the selection, which is triggered by a right mouse
// click at (xPos, yPos).
#define BUFFER_LENGTH 64
void ProcessSelection(int xPos, int yPos)
{
    GLfloat fAspect;
    // Space for selection buffer
    static GLuint selectBuff[BUFFER_LENGTH];
    // Hit counter and viewport storage
    GLint hits, viewport[4];
    // Set up selection buffer
    glSelectBuffer(BUFFER_LENGTH, selectBuff);
    // Get the viewport
    glGetIntegerv(GL_VIEWPORT, viewport);
    // Switch to projection and save the matrix
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    // Change render mode
    glRenderMode(GL_SELECT);
    // Establish new clipping volume to be unit cube around
    // mouse cursor point (xPos, yPos) and extending two pixels
    // in the vertical and horizontal direction
    glLoadIdentity();
    gluPickMatrix(xPos, viewport[3] - yPos + viewport[1], 2,2, viewport);
    // Apply perspective matrix
    fAspect = (float)viewport[2] / (float)viewport[3];
    gluPerspective(45.0f, fAspect, 1.0, 425.0);
    // Draw the scene
    drawScene();
    // Collect the hits
    hits = glRenderMode(GL_RENDER);
    //ProcessObjects finds hit objects and acts on them.
    ProcessObjects(selectBuff, hits);
    // Restore the projection matrix
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    // Go back to modelview for normal rendering
    glMatrixMode(GL_MODELVIEW);
}

Position getClickPos(int x, int y)
{
    GLint viewport[4];
    GLdouble mvmatrix[16], projmatrix[16];
    GLint realy; /* OpenGL y coordinate position */
    GLdouble wx, wy, wz, wx2,wy2,wz2; /* returned world x, y, z coords */

    glGetIntegerv (GL_VIEWPORT, viewport);
    glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
    glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
    /* note viewport[3] is height of window in pixels */
    realy = viewport[3] - (GLint) y - 1;

    gluUnProject ((GLdouble) x, (GLdouble) realy, 0.0, mvmatrix, projmatrix, viewport, &wx, &wy, &wz);

    gluUnProject ((GLdouble) x, (GLdouble) realy, 1.0, mvmatrix, projmatrix, viewport, &wx2, &wy2, &wz2);


    Position groundPos = Position((float)(wx + (wx2 - wx) * (GROUND - wz) / (wz2 - wz)),
                                  (float)(wy + (wy2 - wy) * (GROUND - wz) / (wz2 - wz)));

    return groundPos;
}

void mouse(int x, int y)
{
    //If right mouse button is clicked
    if( SDL_GetMouseState(&x,&y) & SDL_BUTTON_RMASK )
    {
        bool testBlock = true; // tests to see if a turret is clicked as a move position
        Position clickPos = getClickPos(x,y);
        clickPos.Xpos =  roundf(clickPos.Xpos);
        clickPos.Ypos = roundf(clickPos.Ypos);

        //Checks to see if you clicked a turret
        for(int i = 0; i < TurPos.size(); i++)
        {
            if(TurPos[i] == clickPos)
                testBlock = false;

        }

        //Moves the hero if a turret was not clicked, also checks to see if you are enroute to a turret.
        if(heroState == Walking && testBlock)
        {
            CheckBuildPos = false;
            hero.bestPath(TurPos,clickPos);
        }
    }

    //If left mouse button is clicked
    if( SDL_GetMouseState(&x,&y) & SDL_BUTTON_LMASK )
    {
        Position clickPos = getClickPos(x,y);
        clickPos.Xpos =  roundf(clickPos.Xpos);
        clickPos.Ypos = roundf(clickPos.Ypos);

        //Checks to see if a a turret has been input, if so it will place the turret at the mouse click location
        if((Input == BuildStandard ||
                Input == BuildSlow ||
                Input == BuildAoE) &&
                CanBuild &&
                heroState != Building)
        {

            CheckBuildPos = true;
            TurretIsPlaced = Input; //Turns on code in the update functiion in main.cpp
            Input = None;
            hero.bestPath(TurPos,clickPos);
        }
        //Handles Skills for mouse click
        if(Input == SlowSkill)
        {
            hero.UseSkill();
            hero.SetTarget(clickPos);
            Input = None;
        }
        ProcessSelection(x, y);
    }
}

//Checks to see if Turret can be build, if it can, will be built at position x,y
void BuildTurret(Position P)
{
    bool RePathfind = false;
    Position clickPos = P;
    clickPos.Xpos =  roundf(clickPos.Xpos);
    clickPos.Ypos = roundf(clickPos.Ypos);
    for(int k = 0; k < Spawner.getBestPath().size(); k++)
    {
        if(clickPos == Spawner.getBestPath()[k])
            RePathfind = true;
    }
    switch(TurretIsPlaced)
    {
    case BuildStandard :
    {
        turret.push_back(Turret(clickPos) );
        turret[turret.size() - 1].SetIndex(turret.size() - 1);
        TurPos.push_back(clickPos);

        break;
    }
    case BuildSlow :
    {
        turret.push_back(Turret(clickPos,Slow) );
        turret[turret.size() - 1].SetIndex(turret.size() - 1);
        TurPos.push_back(clickPos);

        break;

    }
    case BuildAoE :
    {
        turret.push_back(Turret(clickPos,AoE));
        turret[turret.size() - 1].SetIndex(turret.size() - 1);
        TurPos.push_back(clickPos);

        break;
    }

    }

    CheckWall(clickPos); // Finds a new best path for minion if the position is in the wayfo its path
    if(RePathfind) // Will check to see if the positions changed, if so it wass change all minion
    {
        Spawner.bestPath(TurPos, goal);
        std::vector < std::vector <Position> > paths;
        paths.push_back(Spawner.getBestPath());
        for(int j=0; j < Min.size(); j++)
        {
            ///if it is in the path, check to see where in the path it is.
            bool foundpath = false;
            for(int k = 0; k < paths.size() && !foundpath; k++)
            {
                if(find(paths[k].begin(),paths[k].end(),Min[j].getBestPath()[k]) != paths[k].end())  //if we find that there is a path
                {
                    std::vector<Position> tempvector =
                        std::vector<Position>(find(paths[k].begin(),paths[k].end(),Min[j].getBestPath()[k]), paths[k].end() );
                    Min[j].setPath(tempvector);
                    foundpath = true;
                }
            }
            if(!foundpath)  // There is not path
            {
                Min[j].bestPath(TurPos, goal);
                paths.push_back(Min[j].getBestPath());
            }
        }

    }
    heroState = Walking;

}

//returns tru if there is obstruction, false if its clear
bool CheckObstruction(Position P)
{

    Position clickPos = P;
    //Checks to see if you are in bound of the building area
    if(clickPos.Ypos >10 ||clickPos.Ypos < -10 || clickPos.Xpos > 9 || clickPos.Xpos< -9 )
        return true; // Need to change values once map is made
    for(int i = 0; i < Min.size(); i++)
    {
        if(clickPos*= Min[i].getPos())
        {
            return true;

        }
    }
    for(int i = 0; i < TurPos.size(); i++)
    {
        if(clickPos == TurPos[i])
        {
            return true;

        }
    }
    return false;

}

//If walled, returs true
bool CheckWall(Position P)
{
    bool InPath = false; //Returns true if in path
    Position clickPos = P;
    //Will return true if the position is on the testminion path
    for(int i = 0; i < Spawner.getBestPath().size(); i++)
    {
        if(clickPos == Spawner.getBestPath()[i])
            InPath = true;
    }

    //If you arent in the path of the minion and the path is existant, then there is no way it could be walled
    if(!InPath && Spawner.getBestPath().size() > 0)
        return false;
    if(clickPos == goal)
    {
        return true; // if you are trying to build at the destination fo the mininions
    }
    //Makes sure you can't build to the side of spawn, I was having bugs, hardcoded a fix.
       ///Fix this if need be.
    if(clickPos == SpawnPoint ||
       clickPos == Position(SpawnPoint.Xpos - 1, SpawnPoint.Ypos) ||
       clickPos == Position(SpawnPoint.Xpos + 1, SpawnPoint.Ypos) ||
       clickPos == Position(SpawnPoint.Xpos, SpawnPoint.Ypos -1) ||
       clickPos == Position(SpawnPoint.Xpos , SpawnPoint.Ypos + 1))
    {
        return true; // if you are trying to build at the  mininions spawn
    }
    TurPos.push_back(clickPos);
    //If there is no path, there is a wall
    if(Spawner.testPath(TurPos, goal).empty())
    {
        TurPos.erase(TurPos.end()-1);
        return true;
    }
    TurPos.erase(TurPos.end()-1);
    return false;
}

void handleKeypress()
{
    Uint8 *keyState = SDL_GetKeyState(NULL);

    //If escape is pressed and left alt is pressed
    if( keyState[ SDLK_ESCAPE ] && keyState[ SDLK_LALT ])
    {
        quit = true;
    }

    if(heroState == Walking)
    {
        //If escape is pressed
        if( keyState[ SDLK_ESCAPE ])
        {
            Input = None;
        }
        //If 't' is pressed
        if( keyState[ SDLK_t ])
        {
            Input = BuildStandard;
        }
        //If 's' is pressed
        if( keyState[ SDLK_s ])
        {
            Input = BuildSlow;
        }
        //If 'a' is pressed
        if( keyState[ SDLK_a ])
        {
            Input = BuildAoE;
        }
        //If '1' is pressed
        if( keyState[ SDLK_1 ])
        {
            if(hero.CheckSlowSkillTimer())
                Input = SlowSkill;
        }
        //If space is pressed
        if( keyState[ SDLK_SPACE ])
        {
            gameCamera.ReturnToOrigin(hero.getPos());
        }
    }
}




